<?php
/**
 * PollingRepo.php
 *
 * @copyright   2023 thingsworld.com.cn - All Rights Reserved
 * @link        http://thingsworld.com.cn/
 * @author      stiffer.chen <chenl@thingsworld.com.cn>
 * @created     2023-09-02 11:13:10
 * @modified    2023-09-02 11:13:10
 *
 */

namespace App\Repositories;

use App\Http\Resources\PollingResource;
use App\Models\Customer;
use App\Models\Notification;
use App\Models\Polling;
use App\Models\UserModel;
use App\Services\DeptService;
use App\Services\UserService;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Monolog\Handler\IFTTTHandler;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Ramsey\Uuid\Exception\TimeSourceException;

class PollingRepo
{

    const STATUS_CREATE = 'create'; // 已创建
    const STATUS_CREATE_TEXT = '已创建'; // 已创建
    const STATUS_ASSIGN = 'assign'; // 已指派
    const STATUS_ASSIGN_TEXT = '已指派'; // 已指派
    const STATUS_ACCEPT = 'accept'; // 已接受
    const STATUS_ACCEPT_TEXT = '已接受'; // 已接受
    const STATUS_START = 'start'; // 已开始
    const STATUS_START_TEXT = '已开始'; // 已开始
    const STATUS_COMPLETE = 'complete'; // 已完成
    const STATUS_COMPLETE_TEXT = '已完成'; // 已完成

    const ROLE_CREATE = 'create';
    const ROLE_CREATE_TEXT = '我创建';
    const ROLE_MANAGER = 'manager';
    const ROLE_MANAGER_TEXT = '我负责';

    public static function params()
    {
        $status = [
            ['code' => self::STATUS_ASSIGN, 'title' => self::STATUS_ASSIGN_TEXT],
            ['code' => self::STATUS_ACCEPT, 'title' => self::STATUS_ACCEPT_TEXT],
            ['code' => self::STATUS_START, 'title' => self::STATUS_START_TEXT],
            ['code' => self::STATUS_COMPLETE, 'title' => self::STATUS_COMPLETE_TEXT],
        ];

        $role = [
            ['code' => self::ROLE_CREATE, 'title' => self::ROLE_CREATE_TEXT],
            ['code' => self::ROLE_MANAGER, 'title' => self::ROLE_MANAGER_TEXT],
        ];

        $result = [
            ['code' => '0', 'title' => '异常'],
            ['code' => '1', 'title' => '正常'],
        ];

        $dateType = [
            ['code' => 'day', 'title' => '今日'],
            ['code' => 'week', 'title' => '本周'],
            ['code' => 'month', 'title' => '本月'],
        ];

        return [
            'status' => $status,
            'role' => $role,
            'result' => $result,
            'date_type' => $dateType
        ];
    }

    private static function exist($id)
    {
        $polling = Polling::find($id);
        if (empty($polling) || $polling->mark == 0) {
            throw new \Exception('巡检任务不存在');
        }
    }

    public static function process($id)
    {
        $detail = self::getDetail($id);
        $detail = $detail->toArray($detail);
        return $detail['histories'];
    }

    public static function accept($id)
    {
        self::exist($id);
        $polling = Polling::find($id);
        if (!$polling->btn_accept) {
            throw new \Exception('当前状态不允许执行该操作');
        }

        $status = self::STATUS_ACCEPT;
        self::addHistory($id, $status);

        self::sendNotify([$polling->create_user_id], $id, "巡检任务响应通知", "巡检任务 #" . $polling->number . " 已被接受");

        return self::getDetail($id);
    }

    public static function start($id)
    {
        self::exist($id);
        $polling = Polling::find($id);
        if (!$polling->btn_start) {
            throw new \Exception('当前状态不允许执行该操作');
        }

        $status = self::STATUS_START;
        self::addHistory($id, $status);

        self::sendNotify([$polling->create_user_id], $id, "巡检任务开始通知", "巡检任务 #" . $polling->number . " 已开始处理");

        return self::getDetail($id);
    }

    public static function receipt($id)
    {
        self::exist($id);
        $polling = Polling::find($id);
        if (!$polling->btn_complete) {
            throw new \Exception('当前状态不允许执行该操作');
        }

        $data = self::completeData();
        $images = $data['images'];
        $rImages = [];
        if ($images) {
            foreach ($images as $image) {
                $rImages[] = new Polling\Image([
                    'image' => $image
                ]);
            }
        }

        $polling->acted = $data['acted'];
        $polling->exception = $data['exception'];
        $polling->complete_at = $data['complete_at'];
        $polling->complete_description = $data['complete_description'];
        $polling->customer_sign = $data['customer_sign'];
        $polling->saveOrFail();

        if ($rImages) {
            $polling->images()->saveMany($rImages);
        }

        self::addHistory($id, self::STATUS_COMPLETE);

        self::sendNotify([$polling->create_user_id], $id, "巡检任务完成通知", "巡检任务 #" . $polling->number . " 已完成");

        return self::getDetail($id);
    }

    public static function delete($id)
    {
        self::exist($id);
        $polling = Polling::find($id);
        if (!$polling->btn_delete) {
            throw new \Exception('当前状态不允许删除');
        }
        $polling->mark = 0;
        $polling->saveOrFail();
    }

    public static function counts()
    {
        $counts = Polling::query()
            ->select(['status', DB::raw('count("status") as count')])
            ->where('mark', '=', 1)
            ->groupBy('status')
            ->get()
            ->keyBy('status')
            ->toArray();

        $params = self::params();
        $statusParams = $params['status'];
        $results = [];
        $results[] = [
            'code' => 'all',
            'title' => '全部',
            'count' => collect($counts)->sum('count')
        ];
        foreach ($statusParams as $param) {
            $currentCount = $counts[$param['code']] ?? [];
            $itemCount = $currentCount['count'] ?? 0;
            $results[] = [
                'code' => $param['code'],
                'title' => $param['title'],
                'count' => $itemCount
            ];
        }
        return $results;
    }

    public static function getStatusText($status)
    {
        switch ($status) {
            case self::STATUS_CREATE:
                return self::STATUS_CREATE_TEXT;
            case self::STATUS_ASSIGN:
                return self::STATUS_ASSIGN_TEXT;
            case self::STATUS_ACCEPT:
                return self::STATUS_ACCEPT_TEXT;
            case self::STATUS_START:
                return self::STATUS_START_TEXT;
            case self::STATUS_COMPLETE:
                return self::STATUS_COMPLETE_TEXT;
            default:
                return '';
        }
    }

    public static function getList()
    {
        $status = trim(request('status', ''));
        $role = trim(request('role', ''));
        $result = request('result', '');
        $number = trim(request('number', ''));
        $planStart = trim(request('plan_start', ''));
        $dateType = trim(request('date_type', ''));

        if ($planStart) {
            try {
                $planStart = Carbon::parse($planStart)->toDateTimeString();
            } catch (\Exception $e) {
                $planStart = '';
            }
        }
        $planEnd = trim(request('plan_end', ''));
        if ($planEnd) {
            try {
                $planEnd = Carbon::parse($planEnd)->toDateTimeString();
            } catch (\Exception $e) {
                $planEnd = '';
            }
        }
        $userId = registry('user_id');

        $limit = intval(request('limit'));
        if ($limit <= 0) {
            $limit = 10;
        }

        $params = self::params();

        $allStatus = $params['status'];
        $statusCode = collect($allStatus)->pluck('code')->toArray();

        $builder = Polling::query()
            ->where('polling.mark', '=', 1)
            ->orderByDesc('update_time')
            ->orderByDesc('create_time');

        if ($dateType == 'day') {
            $start = Carbon::now()->startOfDay()->timestamp;
            $end = Carbon::now()->endOfDay()->timestamp;
            $builder->where('polling.create_time', '>=', $start)
                ->where('polling.create_time', '<=', $end);
        } elseif ($dateType == 'week') {
            $start = Carbon::now()->startOfWeek()->timestamp;
            $end = Carbon::now()->endOfWeek()->timestamp;
            $builder->where('polling.create_time', '>=', $start)
                ->where('polling.create_time', '<=', $end);
        } elseif ($dateType == 'month') {
            $start = Carbon::now()->startOfMonth()->timestamp;
            $end = Carbon::now()->endOfMonth()->timestamp;
            $builder->where('polling.create_time', '>=', $start)
                ->where('polling.create_time', '<=', $end);
        }

        if ($number) {
            $builder->where('polling.number', 'like', "%{$number}%");
        }
        if (in_array($status, $statusCode)) {
            $builder->where('polling.status', '=', $status);
        }

        if ($role == self::ROLE_CREATE) {
            $builder->where('polling.create_user_id', '=', $userId);
        }
        if ($role == self::ROLE_MANAGER) {
            $builder->where('polling.user_id', '=', $userId);
        }
        if ($result === '0') {
            $builder->where('polling.exception', '=', '0')
                ->where('status', '=', self::STATUS_COMPLETE);
        }
        if ($result === '1') {
            $builder->where('polling.exception', '=', '1')
                ->where('status', '=', self::STATUS_COMPLETE);
        }
        if ($planStart) {
            $builder->where('polling.plan_at', '>=', $planStart);
        }
        if ($planEnd) {
            $builder->where('polling.plan_at', '<=', $planEnd);
        }

        $builder->with(['histories']);

        $export = intval(request('export', ''));
        if ($export) {
            return self::getExportData($builder);
        }

        $polling = $builder->paginate($limit);
        $polling->transform(function ($item) {
            return new PollingResource($item);
        });
        return $polling;
    }

    private static function getExportData($builder)
    {
        $idStr = trim(request('ids', ''));
        $idParam = [];
        if ($idStr) {
            $idArr = explode(',', $idStr);
            foreach ($idArr as $item) {
                $item = intval($item);
                if ($item > 0) {
                    $idParam[] = $item;
                }
            }
        }
        if ($idParam) {
            $builder->whereIn('id', $idParam);
        }
        $exportData = $builder->get()->toArray();
        return self::exportData($exportData);
    }

    private static function exportData($data)
    {
        $pathPrefix = 'downloads/polling/' . date('Y/m/d/');
        $prefix = public_path() . '/' . $pathPrefix;
        $excelFileName = 'polling-' . md5(date('YmdHis') . microtime()) . '.xlsx';

        if (!is_dir($prefix)) {
            mkdir($prefix, 0777, true);
        }
        $excelFilePath = $prefix . $excelFileName;
        $spreadsheet = new Spreadsheet();
        $sheet = new Worksheet($spreadsheet, "巡检任务");
        $spreadsheet->addSheet($sheet);
        $spreadsheet->removeSheetByIndex(0);

        $excelData[] = [
            '序号',
            '巡检编号',
            '客户',
            '联系人',
            '电话',
            '客户地址',
            '计划时间',
            '负责人',
            '状态',
            '巡检状态',
            '巡检结果',
            '巡检时间',
            '创建方式',
            '创建人',
            '创建时间'
        ];

        foreach ($data as $item) {
            $histories = collect($item['histories'])->keyBy('status')->toArray();
            $startHistory = $histories[self::STATUS_START] ?? [];
            $startTime = '';
            if ($startHistory) {
                $startTime = Carbon::parse($startHistory['create_time'])->toDateTimeString();
            }
            $excelData[] = [
                $item['id'],
                $item['number'],
                $item['customer_name'],
                $item['customer_user_name'],
                ' ' . $item['customer_user_telephone'],
                $item['customer_address'],
                $item['plan_at'],
                $item['user_name'],
                $item['status_text'],
                $item['exception'] ? '异常' : '正常',
                $item['acted'] ? '已巡检' : '未巡检',
                $startTime,
                $item['type'] == self::CREATE_USER ? '手动创建' : '自动创建',
                $item['create_user_name'],
                Carbon::parse($item['create_time'])->toDateTimeString()
            ];
        }

        $sheet->fromArray($excelData);
        $writer = new Xlsx($spreadsheet);
        $writer->save($excelFilePath);
        return env('APP_URL') . '/' . $pathPrefix . $excelFileName;
    }

    const CREATE_USER = 'user'; // 手动创建
    const CREATE_TASK = 'task'; // 任务创建
    // 创建巡检任务
    public static function store()
    {
        $data = self::storeData();
        return self::storeAction($data);
    }

    // 保存巡检任务(计划任务通过该方法保存)
    public static function storeAction($data)
    {
        $main = $data['main'];
        $polling = new Polling();
        $polling->fill($main);
        $polling->saveOrFail();
        $pollingId = $polling->id;

        self::addHistory($pollingId, self::STATUS_CREATE);
        self::addHistory($pollingId, self::STATUS_ASSIGN);

        self::sendNotify([$main['user_id']], $pollingId, "新巡检任务通知", "你有一条新巡检任务需要处理");

        return self::getDetail($pollingId);
    }

    private static function addHistory($id, $status)
    {
        $polling = Polling::find($id);
        if (empty($polling)) {
            return;
        }
        $polling->status = $status;
        $polling->saveOrFail();

        $number = $polling->number;
        $historyContent = '';
        $actorId = 0;
        $actorName = '';
        $userName = $polling->user_name;
        if ($status == self::STATUS_CREATE) {
            $historyContent = "提交了巡检任务 #{$number}";
            $actorId = $polling->create_user_id;
            $actorName = $polling->create_user_name;
        } elseif ($status == self::STATUS_ASSIGN) {
            $historyContent = "把巡检任务 #{$number} 指派给了 {$userName}";
            $actorId = $polling->create_user_id;
            $actorName = $polling->create_user_name;
        } elseif ($status == self::STATUS_ACCEPT) {
            $historyContent = "巡检任务 #{$number} 负责人已接受";
            $actorId = $polling->user_id;
            $actorName = $polling->user_name;
        } elseif ($status == self::STATUS_START) {
            $historyContent = "开始巡检任务 #{$number}";
            $actorId = $polling->user_id;
            $actorName = $polling->user_name;
        } elseif ($status == self::STATUS_COMPLETE) {
            $historyContent = "巡检任务 #{$number} 负责人已结束";
            $actorId = $polling->user_id;
            $actorName = $polling->user_name;
        }

        $historyData = [
            'polling_id' => $id,
            'status' => $status,
            'actor_id' => $actorId,
            'actor_name' => $actorName,
            'content' => $historyContent
        ];

        $history = new Polling\History();
        $history->fill($historyData);
        $history->saveOrFail();
    }

    public static function getDetail($id)
    {
        $polling = Polling::with(['histories', 'images'])->find($id);
        if (empty($polling) or !$polling->mark) {
            throw new \Exception('巡检任务不存在');
        }
        return new PollingResource($polling);
    }

    private static function sendNotify($userIds, $pollingId, $title, $content)
    {
        NotificationRepo::multiStore($userIds, $pollingId, NotificationRepo::TYPE_POLLING, $title, $content);
    }

    private static function completeData()
    {
        $acted = intval(request('acted', ));
        if ($acted) {
            $acted = 1;
        }
        $exception = intval(request('exception', ''));
        if ($exception) {
            $exception = 1;
        }
        $completeAt = trim(request('complete_at', ''));
        if (!$completeAt) {
            throw new \Exception('当前状态不允许执行该操作');
        }
        try {
            $completeAt = Carbon::parse($completeAt)->toDateTimeString();
        } catch (\Exception $e) {
            throw new \Exception('质检日期格式错误');
        }
        $completeDescription = trim(request('complete_description', ''));
        $customerSign = trim(request('customer_sign', ''));

        $images = [];
        $pImages = request('images', []);
        foreach ($pImages as $image) {
            $image = trim($image);
            $images[] = $image;
        }

        return [
            'acted' => $acted,
            'exception' => $exception,
            'complete_at' => $completeAt,
            'complete_description' => $completeDescription,
            'customer_sign' => $customerSign,
            'images' => $images
        ];
    }

    private static function storeData()
    {
        $userInfo = (new UserService())->getUserInfo(registry('user_id'));
        $isManager = $userInfo['data']['is_manager'] ?? false;
        if (!$isManager) {
            throw new \Exception('仅区域经理可以创建巡检任务');
        }

        $customerId = intval(request('customer_id', ''));
        $customer = Customer::find($customerId);
        if (empty($customer)) {
            throw new \Exception('客户信息不存在');
        }
        $customerUserId = intval(request('customer_user_id', ''));
        $customerUser = Customer\User::find($customerUserId);
        if (empty($customerUser)) {
            throw new \Exception('客户联系人信息不存在');
        }
        if ($customerUser->customer_id != $customerId) {
            throw new \Exception('所选联系人非当前客户所属');
        }

        $customerName = $customer->name;
        $customerAddress = $customer->province_name . $customer->city_name . $customer->county_name . $customer->address;

        $customerUsername = $customerUser->name;
        $customerUserTelephone = $customerUser->phone;

        $planAt = trim(request('plan_at', ''));
        if (empty($planAt)) {
            throw new \Exception('请选择计划时间');
        } else {
            try {
                $planAt = Carbon::parse($planAt)->toDateTimeString();
            } catch (\Exception $e) {
                throw new \Exception("计划时间格式错误");
            }
        }

        if ($planAt <= Carbon::now()->toDateTimeString()) {
            throw new \Exception("计划时间需大于当前时间");
        }

        $description = trim(request('description', ''));

        $userId = intval(request('user_id'));
        $user = UserModel::find($userId);
        if (empty($user)) {
            throw new \Exception('请选择负责人');
        }
        $timeoutHour = floatval(request('timeout_hour', ''));
        if ($timeoutHour <= 0) {
            $timeoutHour = 24;
        }

        $number = self::getPollingNumber();

        $createUserId = registry('user_id');
        $createUsername = registry('user')->realname ?? '';
        $status = self::STATUS_ASSIGN;

        $customerSign = trim(request('customer_sign', ''));

        $main = [
            'number' => $number,
            'type' => self::CREATE_USER,
            'status' => $status,
            'customer_id' => $customerId,
            'customer_name' => $customerName,
            'customer_user_id' => $customerUserId,
            'customer_user_name' => $customerUsername,
            'customer_user_telephone' => $customerUserTelephone,
            'customer_address' => $customerAddress,
            'customer_sign' => $customerSign,
            'plan_at' => $planAt,
            'description' => $description,
            'user_id' => $userId,
            'user_name' => $user->realname ?? '',
            'create_user_id' => $createUserId,
            'create_user_name' => $createUsername,
            'timeout_hour' => $timeoutHour
        ];
        $history = [
            'actor_id' => $createUserId,
            'actor_name' => $createUsername,
            'status' => $status,
            'to_actor_id' => $userId,
            'to_actor_name' => $user->realname ?? '',
            'content' => "把巡检任务 #{$number} 分配给了 " . $user->realname ?? ''
        ];

        return [
            'main' => $main,
            'history' => $history
        ];
    }

    public static function getPollingNumber()
    {
        $prefix = 'P_';
        $number = $prefix . date('Ymd') . Str::random(8);
        $numberPolling = Polling::byNumber($number)->first();
        if (empty($numberPolling)) {
            return $number;
        }
        return self::getPollingNumber();
    }
}